iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 19
1
Modern Web

React.js 從 【0】 到【1】推坑計畫 系列 第 19

【Day 19】可能不需要 redux - useReducer

  • 分享至 

  • xImage
  •  

今天要介紹另一個 hooks - useReducer。
光看第一行有些讀者可能就會有一些疑問了,udeReducer?看起來跟 redux 的 reducer 有 87分像欸!
沒錯,是真的在用途上滿像的,這也引起許多關於取代 redux 的爭論。

首先我們來看看官方怎麼描述這個新方法

是 useState 的另一種替代。它接受(state, action) => newState,并且返回了一个與當前state成對的dispatch的方法。(如果你熟悉 Redux ,你也很快就能理解它是怎麼工作的。)useState 實際上執行的也是一個 useReducer。這意味著 useReducer 是更原生的,你能在任何使用 useState 的地方都替换成使用 useReducer。

我們來看看最簡單的用法,依然以 counter 為範例:

import React, { useReducer } from 'react';

const initialState = 0;
const reducer = (state, action) => {
  switch (action) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset': return 0;
    default: return state;
  }
};

const Counter = () => {
  const [count, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      {count}
      <button onClick={() => dispatch('increment')}>+1</button>
      <button onClick={() => dispatch('decrement')}>-1</button>
      <button onClick={() => dispatch('reset')}>reset</button>
    </div>
  );
};

export default Counter;

對比 redux 的 reducer,可能有讀者會發現這邊的 state 是一個數字而不是之前 redux reducer 使用的物件。

當然, state 要是一個物件也是可以的:

const initialState = {
  count_1: 0,
  count_2: 0,
};
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment_1':
      return { ...state, count1: state.count_1 + 1 };
    case 'decrement_1':
      return { ...state, count1: state.count_1 - 1 };
    case 'set1':
      return { ...state, count1: action.count };
    case 'increment_2':
      return { ...state, count2: state.count_2 + 1 };
    case 'decrement_2':
      return { ...state, count2: state.count_2 - 1 };
    case 'set2':
      return { ...state, count2: action.count };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      <div>
        {state.count_1}
        <button onClick={() => dispatch({ type: 'increment_1' })}>+1</button>
        <button onClick={() => dispatch({ type: 'decrement_1' })}>-1</button>
        <button onClick={() => dispatch({ type: 'set1', count: 0 })}>reset</button>
      </div>
      <div>
        {state.count_2}
        <button onClick={() => dispatch({ type: 'increment_2' })}>+1</button>
        <button onClick={() => dispatch({ type: 'decrement_2' })}>-1</button>
        <button onClick={() => dispatch({ type: 'set2', count: 0 })}>reset</button>
      </div>
    </>
  );
};

不同 dispatch 共用同一 reducer

const initialState = 0;
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'set': return action.count;
    default: return state;
  }
};

const Counter = () => {
  const [counter1, dispatch_1] = useReducer(reducer, initialState);
  const [counter2, dispatch_2] = useReducer(reducer, initialState);
  return (
    <>
      <div>
        {counter1}
        <button onClick={() => dispatch_1({ type: 'increment' })}>+1</button>
        <button onClick={() => dispatch_1({ type: 'decrement' })}>-1</button>
        <button onClick={() => dispatch_1({ type: 'set', count: 0 })}>reset</button>
      </div>
      <div>
        {counter2}
        <button onClick={() => dispatch_2({ type: 'increment' })}>+1</button>
        <button onClick={() => dispatch_2({ type: 'decrement' })}>-1</button>
        <button onClick={() => dispatch_2({ type: 'set', count: 0 })}>reset</button>
      </div>
    </>
  );
};

特別注意,action 是一個物件,state 是單純的數字。
counter1 與 counter2 擁有各自的 dispatch 方法,卻是共用同一個 reducer。

看到這你可能會說:就算 usereducer 可以取代 redux 的 reducer 好了,我還是得依靠 redux 擁有 global 的 store 啊!
別急,單靠 useReducer 的確不夠,明天將介紹 contextAPI 與 useReducer 做結合來實現 redux 的功能,到時候可能真的就不一定需要 redux 了呢!(不一定而已)


上一篇
【Day 18】Redux 實戰演練(2)
下一篇
【Day 20】可能不需要redux (2) - contextAPI
系列文
React.js 從 【0】 到【1】推坑計畫 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言